"""
流程图执行引擎
支持条件判断、循环控制的场景执行逻辑
"""

import json
import time
import asyncio
import logging
import uuid
from typing import Dict, Any, List, Optional, Tuple, Set
from datetime import datetime
import httpx
import jsonpath_ng
import re
from sqlalchemy.orm import Session

from ..models.api_automation import (
    ApiInterface, TestScenario, FlowNode, FlowConnection,
    ScenarioExecution, StepExecutionResult, EnvironmentConfig
)
from ..schemas.api_automation import (
    ScenarioExecuteRequest, ScenarioExecuteResponse
)

logger = logging.getLogger(__name__)


class FlowExecutionEngine:
    """流程图执行引擎"""
    
    def __init__(self):
        self.http_client = httpx.AsyncClient(
            timeout=httpx.Timeout(300.0),  # 设置超时时间为5分钟
            limits=httpx.Limits(max_keepalive_connections=20, max_connections=100)
        )
        self.execution_contexts = {}  # 存储执行上下文
    
    async def close(self):
        """关闭HTTP客户端"""
        await self.http_client.aclose()
    
    async def execute_scenario_flow(self, request: ScenarioExecuteRequest, db: Session, 
                                   user_id: int) -> ScenarioExecuteResponse:
        """执行基于流程图的测试场景"""
        try:
            # 获取场景信息
            scenario = db.query(TestScenario).filter(TestScenario.id == request.scenario_id).first()
            if not scenario:
                return ScenarioExecuteResponse(
                    execution_id="",
                    status="failed",
                    message="场景不存在"
                )
            
            # 获取流程图节点和连接
            nodes = db.query(FlowNode).filter(FlowNode.scenario_id == scenario.id).all()
            connections = db.query(FlowConnection).filter(FlowConnection.scenario_id == scenario.id).all()
            
            if not nodes:
                return ScenarioExecuteResponse(
                    execution_id="",
                    status="failed",
                    message="场景没有配置流程图"
                )
            
            # 创建执行记录
            execution_id = str(uuid.uuid4())
            execution = ScenarioExecution(
                execution_id=execution_id,
                scenario_id=scenario.id,
                executor_id=user_id,
                status="pending",
                total_steps=len(nodes),
                start_time=datetime.utcnow()
            )
            db.add(execution)
            db.commit()
            
            # 异步执行流程图
            if request.debug_mode:
                # 调试模式：同步执行并返回详细结果
                await self._execute_flow_graph(execution.id, scenario, nodes, connections, request, db)
            else:
                # 正常模式：异步执行
                asyncio.create_task(self._execute_flow_graph(execution.id, scenario, nodes, connections, request, db))
            
            return ScenarioExecuteResponse(
                execution_id=execution_id,
                status="running",
                message="场景执行已启动"
            )
            
        except Exception as e:
            logger.error(f"流程图执行启动失败: {str(e)}")
            return ScenarioExecuteResponse(
                execution_id="",
                status="failed",
                message=f"执行启动失败: {str(e)}"
            )
    
    async def _execute_flow_graph(self, execution_id: int, scenario: TestScenario, 
                                 nodes: List[FlowNode], connections: List[FlowConnection],
                                 request: ScenarioExecuteRequest, db: Session):
        """执行流程图"""
        execution = db.query(ScenarioExecution).filter(ScenarioExecution.id == execution_id).first()
        if not execution:
            return
        
        try:
            # 更新执行状态
            execution.status = "running"
            db.commit()
            
            # 构建流程图结构
            flow_graph = self._build_flow_graph(nodes, connections)
            
            # 初始化执行上下文
            context = {
                'variables': request.variables or {},
                'environment': request.environment_id,
                'scenario_variables': scenario.environment_variables or {},
                'execution_id': execution_id,
                'debug_mode': request.debug_mode,
                'loop_stack': [],  # 循环栈
                'visited_nodes': set(),  # 已访问的节点（防止无限循环）
                'max_iterations': 1000,  # 最大迭代次数
                'current_iteration': 0
            }
            
            # 查找开始节点
            start_nodes = [node for node in nodes if node.node_type == 'start']
            if not start_nodes:
                raise Exception("未找到开始节点")
            
            # 从开始节点执行
            start_node = start_nodes[0]
            await self._execute_node(start_node, flow_graph, context, db)
            
            # 更新执行结果
            execution.status = "completed"
            execution.end_time = datetime.utcnow()
            execution.duration = (execution.end_time - execution.start_time).total_seconds()
            
            # 统计执行结果
            step_results = db.query(StepExecutionResult).filter(
                StepExecutionResult.execution_id == execution_id
            ).all()
            
            execution.passed_steps = len([r for r in step_results if r.status == "passed"])
            execution.failed_steps = len([r for r in step_results if r.status == "failed"])
            execution.skipped_steps = len([r for r in step_results if r.status == "skipped"])
            
            if execution.failed_steps > 0:
                execution.status = "failed"
            
            db.commit()
            
        except Exception as e:
            logger.error(f"流程图执行失败: {str(e)}")
            execution.status = "failed"
            execution.error_message = str(e)
            execution.end_time = datetime.utcnow()
            if execution.start_time:
                execution.duration = (execution.end_time - execution.start_time).total_seconds()
            db.commit()
    
    def _build_flow_graph(self, nodes: List[FlowNode], connections: List[FlowConnection]) -> Dict[str, Any]:
        """构建流程图结构"""
        # 创建节点映射
        node_map = {node.id: node for node in nodes}
        
        # 创建连接映射
        outgoing_connections = {}
        incoming_connections = {}
        
        for conn in connections:
            if not conn.is_enabled:
                continue
                
            # 出边
            if conn.source_node_id not in outgoing_connections:
                outgoing_connections[conn.source_node_id] = []
            outgoing_connections[conn.source_node_id].append(conn)
            
            # 入边
            if conn.target_node_id not in incoming_connections:
                incoming_connections[conn.target_node_id] = []
            incoming_connections[conn.target_node_id].append(conn)
        
        return {
            'nodes': node_map,
            'outgoing_connections': outgoing_connections,
            'incoming_connections': incoming_connections
        }
    
    async def _execute_node(self, node: FlowNode, flow_graph: Dict[str, Any], 
                           context: Dict[str, Any], db: Session) -> bool:
        """执行单个节点"""
        if not node.is_enabled:
            return True
        
        # 防止无限循环
        context['current_iteration'] += 1
        if context['current_iteration'] > context['max_iterations']:
            raise Exception("执行超过最大迭代次数，可能存在无限循环")
        
        logger.info(f"执行节点: {node.name} (类型: {node.node_type})")
        
        try:
            success = True
            
            if node.node_type == 'start':
                success = await self._execute_start_node(node, context, db)
            elif node.node_type == 'end':
                success = await self._execute_end_node(node, context, db)
            elif node.node_type == 'api_call':
                success = await self._execute_api_node(node, context, db)
            elif node.node_type == 'condition':
                success = await self._execute_condition_node(node, flow_graph, context, db)
                return success  # 条件节点自己处理后续流程
            elif node.node_type == 'loop':
                success = await self._execute_loop_node(node, flow_graph, context, db)
                return success  # 循环节点自己处理后续流程
            else:
                logger.warning(f"未知节点类型: {node.node_type}")
                success = True
            
            # 执行下一个节点
            if success or not node.continue_on_failure:
                await self._execute_next_nodes(node, flow_graph, context, db, success)
            
            return success
            
        except Exception as e:
            logger.error(f"节点执行失败 {node.name}: {str(e)}")
            
            # 记录执行结果
            await self._record_step_result(
                node, context, db, "failed", error_message=str(e)
            )
            
            if node.continue_on_failure:
                await self._execute_next_nodes(node, flow_graph, context, db, False)
                return False
            else:
                raise e
    
    async def _execute_start_node(self, node: FlowNode, context: Dict[str, Any], db: Session) -> bool:
        """执行开始节点"""
        await self._record_step_result(node, context, db, "passed")
        return True
    
    async def _execute_end_node(self, node: FlowNode, context: Dict[str, Any], db: Session) -> bool:
        """执行结束节点"""
        await self._record_step_result(node, context, db, "passed")
        return True
    
    async def _execute_api_node(self, node: FlowNode, context: Dict[str, Any], db: Session) -> bool:
        """执行API调用节点"""
        if not node.interface_id:
            raise Exception("API节点未配置接口")
        
        # 获取接口信息
        interface = db.query(ApiInterface).filter(ApiInterface.id == node.interface_id).first()
        if not interface:
            raise Exception("接口不存在")
        
        # 获取环境配置
        environment = None
        if context.get('environment'):
            environment = db.query(EnvironmentConfig).filter(
                EnvironmentConfig.id == context['environment']
            ).first()
        
        # 构建请求
        request_data = await self._build_api_request(interface, node, context, environment)
        
        # 发送请求
        start_time = time.time()
        try:
            response = await self.http_client.request(
                method=interface.method,
                url=request_data['url'],
                headers=request_data['headers'],
                params=request_data.get('params'),
                json=request_data.get('json'),
                data=request_data.get('data')
            )
            
            response_time = (time.time() - start_time) * 1000  # 毫秒
            
            # 提取变量
            if node.node_config and node.node_config.get('variableExtractions'):
                await self._extract_variables(response, node.node_config['variableExtractions'], context)
            
            # 记录执行结果
            await self._record_step_result(
                node, context, db, "passed",
                request_url=str(response.url),
                request_method=interface.method,
                request_headers=request_data['headers'],
                request_body=json.dumps(request_data.get('json') or request_data.get('data')),
                response_status_code=response.status_code,
                response_headers=dict(response.headers),
                response_body=response.text,
                response_time=response_time
            )
            
            return True
            
        except Exception as e:
            response_time = (time.time() - start_time) * 1000
            
            await self._record_step_result(
                node, context, db, "failed",
                request_url=request_data['url'],
                request_method=interface.method,
                request_headers=request_data['headers'],
                request_body=json.dumps(request_data.get('json') or request_data.get('data')),
                response_time=response_time,
                error_message=str(e)
            )
            
            raise e
    
    async def _execute_condition_node(self, node: FlowNode, flow_graph: Dict[str, Any], 
                                     context: Dict[str, Any], db: Session) -> bool:
        """执行条件判断节点"""
        config = node.node_config or {}
        
        # 评估条件
        condition_result = await self._evaluate_condition(config, context)
        
        # 记录执行结果
        await self._record_step_result(
            node, context, db, "passed",
            extracted_variables={'condition_result': condition_result}
        )
        
        # 根据条件结果选择下一个节点
        outgoing_connections = flow_graph['outgoing_connections'].get(node.id, [])
        
        for conn in outgoing_connections:
            # 检查连接类型
            if condition_result and conn.connection_type in ['default', 'success']:
                target_node = flow_graph['nodes'][conn.target_node_id]
                await self._execute_node(target_node, flow_graph, context, db)
                break
            elif not condition_result and conn.connection_type == 'failure':
                target_node = flow_graph['nodes'][conn.target_node_id]
                await self._execute_node(target_node, flow_graph, context, db)
                break
        
        return True
    
    async def _execute_loop_node(self, node: FlowNode, flow_graph: Dict[str, Any], 
                                context: Dict[str, Any], db: Session) -> bool:
        """执行循环节点"""
        config = node.node_config or {}
        loop_type = config.get('loopType', 'count')
        
        # 记录循环开始
        await self._record_step_result(node, context, db, "passed")
        
        if loop_type == 'count':
            return await self._execute_count_loop(node, flow_graph, context, db, config)
        elif loop_type == 'while':
            return await self._execute_while_loop(node, flow_graph, context, db, config)
        elif loop_type == 'foreach':
            return await self._execute_foreach_loop(node, flow_graph, context, db, config)
        else:
            raise Exception(f"未知循环类型: {loop_type}")
    
    async def _execute_count_loop(self, node: FlowNode, flow_graph: Dict[str, Any], 
                                 context: Dict[str, Any], db: Session, config: Dict[str, Any]) -> bool:
        """执行计数循环"""
        count = config.get('count', 1)
        index_var = config.get('indexVariable', 'i')
        
        # 查找循环体连接
        loop_body_connections = [
            conn for conn in flow_graph['outgoing_connections'].get(node.id, [])
            if conn.connection_type == 'loop_body'
        ]
        
        # 查找循环结束后的连接
        next_connections = [
            conn for conn in flow_graph['outgoing_connections'].get(node.id, [])
            if conn.connection_type in ['default', 'success']
        ]
        
        for i in range(count):
            # 设置循环变量
            context['variables'][index_var] = i
            
            # 执行循环体
            for conn in loop_body_connections:
                target_node = flow_graph['nodes'][conn.target_node_id]
                try:
                    await self._execute_node(target_node, flow_graph, context, db)
                except Exception as e:
                    if config.get('breakOnFailure', False):
                        break
                    elif not config.get('continueOnFailure', False):
                        raise e
        
        # 执行循环后的节点
        for conn in next_connections:
            target_node = flow_graph['nodes'][conn.target_node_id]
            await self._execute_node(target_node, flow_graph, context, db)
        
        return True
    
    async def _execute_while_loop(self, node: FlowNode, flow_graph: Dict[str, Any], 
                                 context: Dict[str, Any], db: Session, config: Dict[str, Any]) -> bool:
        """执行条件循环"""
        condition = config.get('condition', '')
        max_iterations = config.get('maxIterations', 100)
        
        iteration = 0
        while iteration < max_iterations:
            # 评估循环条件
            if not await self._evaluate_expression(condition, context):
                break
            
            # 执行循环体
            loop_body_connections = [
                conn for conn in flow_graph['outgoing_connections'].get(node.id, [])
                if conn.connection_type == 'loop_body'
            ]
            
            for conn in loop_body_connections:
                target_node = flow_graph['nodes'][conn.target_node_id]
                try:
                    await self._execute_node(target_node, flow_graph, context, db)
                except Exception as e:
                    if config.get('breakOnFailure', False):
                        break
                    elif not config.get('continueOnFailure', False):
                        raise e
            
            iteration += 1
        
        # 执行循环后的节点
        next_connections = [
            conn for conn in flow_graph['outgoing_connections'].get(node.id, [])
            if conn.connection_type in ['default', 'success']
        ]
        
        for conn in next_connections:
            target_node = flow_graph['nodes'][conn.target_node_id]
            await self._execute_node(target_node, flow_graph, context, db)
        
        return True
    
    async def _execute_foreach_loop(self, node: FlowNode, flow_graph: Dict[str, Any], 
                                   context: Dict[str, Any], db: Session, config: Dict[str, Any]) -> bool:
        """执行遍历循环"""
        array_source = config.get('arraySource', 'variable')
        item_var = config.get('itemVariable', 'item')
        index_var = config.get('indexVariable', 'index')
        
        # 获取数组数据
        array_data = []
        if array_source == 'variable':
            var_name = config.get('arrayVariable', '')
            array_data = context['variables'].get(var_name, [])
        elif array_source == 'static':
            try:
                array_data = json.loads(config.get('staticArray', '[]'))
            except:
                array_data = []
        
        if not isinstance(array_data, list):
            array_data = []
        
        # 遍历数组
        for i, item in enumerate(array_data):
            # 设置循环变量
            context['variables'][item_var] = item
            if index_var:
                context['variables'][index_var] = i
            
            # 执行循环体
            loop_body_connections = [
                conn for conn in flow_graph['outgoing_connections'].get(node.id, [])
                if conn.connection_type == 'loop_body'
            ]
            
            for conn in loop_body_connections:
                target_node = flow_graph['nodes'][conn.target_node_id]
                try:
                    await self._execute_node(target_node, flow_graph, context, db)
                except Exception as e:
                    if config.get('breakOnFailure', False):
                        break
                    elif not config.get('continueOnFailure', False):
                        raise e
        
        # 执行循环后的节点
        next_connections = [
            conn for conn in flow_graph['outgoing_connections'].get(node.id, [])
            if conn.connection_type in ['default', 'success']
        ]
        
        for conn in next_connections:
            target_node = flow_graph['nodes'][conn.target_node_id]
            await self._execute_node(target_node, flow_graph, context, db)
        
        return True
    
    async def _execute_next_nodes(self, node: FlowNode, flow_graph: Dict[str, Any], 
                                 context: Dict[str, Any], db: Session, success: bool):
        """执行下一个节点"""
        outgoing_connections = flow_graph['outgoing_connections'].get(node.id, [])
        
        for conn in outgoing_connections:
            if conn.connection_type == 'default' or (success and conn.connection_type == 'success') or (not success and conn.connection_type == 'failure'):
                target_node = flow_graph['nodes'][conn.target_node_id]
                await self._execute_node(target_node, flow_graph, context, db)
                break
    
    async def _build_api_request(self, interface: ApiInterface, node: FlowNode, 
                                context: Dict[str, Any], environment: Optional[EnvironmentConfig]) -> Dict[str, Any]:
        """构建API请求"""
        # 构建URL
        url = interface.url
        if environment and environment.base_url:
            if url.startswith('/'):
                url = environment.base_url.rstrip('/') + url
            elif not url.startswith('http'):
                url = environment.base_url.rstrip('/') + '/' + url.lstrip('/')
        
        # 替换变量
        url = self._replace_variables(url, context['variables'])
        
        # 构建请求头
        headers = dict(interface.headers or {})
        if environment and environment.headers:
            headers.update(environment.headers)
        
        # 应用参数覆盖
        if node.node_config and node.node_config.get('parameterOverrides'):
            for key, value in node.node_config['parameterOverrides'].items():
                value = self._replace_variables(str(value), context['variables'])
                headers[key] = value
        
        # 构建请求数据
        request_data = {
            'url': url,
            'headers': headers
        }
        
        if interface.method.upper() in ['POST', 'PUT', 'PATCH']:
            if interface.body_type == 'json' and interface.body_content:
                try:
                    body = json.loads(interface.body_content)
                    body = self._replace_variables_in_dict(body, context['variables'])
                    request_data['json'] = body
                except:
                    request_data['data'] = self._replace_variables(interface.body_content, context['variables'])
        
        return request_data
    
    async def _evaluate_condition(self, config: Dict[str, Any], context: Dict[str, Any]) -> bool:
        """评估条件"""
        condition_type = config.get('conditionType', 'custom')
        
        if condition_type == 'custom':
            expression = config.get('expression', '')
            return await self._evaluate_expression(expression, context)
        
        # 其他条件类型的实现...
        return True
    
    async def _evaluate_expression(self, expression: str, context: Dict[str, Any]) -> bool:
        """评估JavaScript表达式"""
        if not expression:
            return True
        
        # 简单的表达式评估（生产环境建议使用更安全的方式）
        try:
            # 替换变量
            expression = self._replace_variables(expression, context['variables'])
            
            # 这里应该使用更安全的表达式评估器
            # 暂时使用简单的字符串比较
            if '==' in expression:
                left, right = expression.split('==', 1)
                return left.strip() == right.strip()
            elif '!=' in expression:
                left, right = expression.split('!=', 1)
                return left.strip() != right.strip()
            
            return True
        except:
            return False
    
    async def _extract_variables(self, response: httpx.Response, extractions: Dict[str, Any], context: Dict[str, Any]):
        """提取变量"""
        for var_name, extraction in extractions.items():
            try:
                if extraction['type'] == 'jsonpath':
                    data = response.json()
                    jsonpath_expr = jsonpath_ng.parse(extraction['expression'])
                    matches = [match.value for match in jsonpath_expr.find(data)]
                    if matches:
                        context['variables'][var_name] = matches[0]
                elif extraction['type'] == 'regex':
                    pattern = extraction['expression']
                    match = re.search(pattern, response.text)
                    if match:
                        context['variables'][var_name] = match.group(1) if match.groups() else match.group(0)
                elif extraction['type'] == 'header':
                    header_name = extraction['expression']
                    context['variables'][var_name] = response.headers.get(header_name, '')
            except Exception as e:
                logger.warning(f"变量提取失败 {var_name}: {str(e)}")
    
    def _replace_variables(self, text: str, variables: Dict[str, Any]) -> str:
        """替换文本中的变量"""
        if not isinstance(text, str):
            return text
        
        for var_name, var_value in variables.items():
            text = text.replace(f'{{{var_name}}}', str(var_value))
            text = text.replace(f'{{{{{var_name}}}}}', str(var_value))
        
        return text
    
    def _replace_variables_in_dict(self, data: Any, variables: Dict[str, Any]) -> Any:
        """递归替换字典中的变量"""
        if isinstance(data, dict):
            return {key: self._replace_variables_in_dict(value, variables) for key, value in data.items()}
        elif isinstance(data, list):
            return [self._replace_variables_in_dict(item, variables) for item in data]
        elif isinstance(data, str):
            return self._replace_variables(data, variables)
        else:
            return data
    
    async def _record_step_result(self, node: FlowNode, context: Dict[str, Any], db: Session, 
                                 status: str, **kwargs):
        """记录步骤执行结果"""
        result = StepExecutionResult(
            execution_id=context['execution_id'],
            step_name=node.name,
            step_order=node.id,  # 使用节点ID作为顺序
            status=status,
            request_url=kwargs.get('request_url'),
            request_method=kwargs.get('request_method'),
            request_headers=kwargs.get('request_headers'),
            request_body=kwargs.get('request_body'),
            response_status_code=kwargs.get('response_status_code'),
            response_headers=kwargs.get('response_headers'),
            response_body=kwargs.get('response_body'),
            response_time=kwargs.get('response_time'),
            extracted_variables=kwargs.get('extracted_variables'),
            error_message=kwargs.get('error_message'),
            executed_at=datetime.utcnow()
        )
        
        db.add(result)
        db.commit()


# 创建全局实例
flow_execution_engine = FlowExecutionEngine()